#include "spi_cmd.h"
#include "util_cmd.h"
#include "util_str.h"
#include "uart_peripheral.h"
#include "spi_peripheral.h"
#include "main.h"
#include "stdio.h"
#include "string.h"

#define EVENT_CHANGE_TIME 		(0x01 << 0)

typedef union {
	uint8_t byteArray[SPI_CMD_ARRAY_SIZE];
	uint16_t halfWordArray[SPI_CMD_ARRAY_SIZE/2];
	uint32_t wordArray[SPI_CMD_ARRAY_SIZE/4];
} _SPIArray;

/* comand */
static char *sCmdSpi0 = "spi0";
/* First level parameter */
static char *sParSpeed = "speed";
static char *sParSend = "send";
static char *sParChar = "char";
static char *sParBuf = "buf";
static char *sParRx = "rx";
static char *sParSize = "size";
/* Second level parameter */
static char *sParWidth = "width";
static char *sParTime = "time";

static _SPIArray *sSPIArray = NULL;
static uint8_t *sReceive = NULL;
static uint16_t sSendLen = 0;
static uint16_t sDataWidth = 16;
static uint16_t newTime;
static uint16_t rxSize = 0; /* size unit = byte */
static uint8_t rxEn = 0;

static TimerHandle_t parTimeTimerHandle = NULL;

EventGroupHandle_t SpiCmdEventGroup = NULL;
TaskHandle_t listenTaskHandle = NULL;

static void timerCallback(TimerHandle_t xTimer)
{
	EventBits_t event;
	
	if (sSendLen != 0) {
		if (sDataWidth == 8) {
			HardSPISendByteArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->byteArray, sSendLen);
		}
		else if (sDataWidth == 16) {
			HardSPISendHalfWordArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->halfWordArray, sSendLen);
		}
		else {
			HardSPISendWordArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->wordArray, sSendLen);
		}
		
		event = xEventGroupGetBits(APPEventGroup);
		if (event == APP_EVENT_SPI_CMD_TIME) {
			xTimerChangePeriod(parTimeTimerHandle, newTime, 0);
		}
	}
}

static void listenTask(void *parameter)
{
	uint16_t i;
	uint16_t *p1 = NULL;
	uint32_t *p2 = NULL;
	
	while (1) {
		xEventGroupWaitBits(SpiCmdEventGroup, SPI_CMD_EVENT_RX_DONE, pdTRUE, pdTRUE, portMAX_DELAY);
		xEventGroupClearBits(APPEventGroup, APP_EVENT_SPI_CMD_RX);
		
		taskENTER_CRITICAL();
		UartPuts(UART_COM1, "SPI0 RX:\r\n");
		if (sDataWidth == 8) {
			for (i = 0; i < rxSize; i++) {
				UartPrintf(UART_COM1, "%02X ", sReceive[i]);
				sReceive[i] = 0;
			}
		}
		else if (sDataWidth == 16) {
			p1 = (uint16_t *)sReceive;
			for (i = 0; i < rxSize / 2; i++) {
				UartPrintf(UART_COM1, "%04X ", p1[i]);
				p1[i] = 0;
			}
		}
		else {
			p2 = (uint32_t *)sReceive;
			for (i = 0; i < rxSize / 4; i++) {
				UartPrintf(UART_COM1, "%08X ", p2[i]);
				p2[i] = 0;
			}
		}
		taskEXIT_CRITICAL();
	}
}

static void showStatus(void)
{
	UartPuts(UART_COM1, "SPI0 Status:\r\n");
	UartPrintf(UART_COM1, "speed = %d\r\n", HardSPIGetSpeed(HARD_SPI_CH0));
	UartPrintf(UART_COM1, "width = %d\r\n", HardSPIGetWidth(HARD_SPI_CH0));
	UartPrintf(UART_COM1, "rxen = %d\r\n", rxEn);
	UartPrintf(UART_COM1, "rx size = %d\r\n", rxSize);
}

static void cmdCallback(CmdParamTypeDef *param)
{
	static uint8_t clearArray = 0;
	
	uint16_t receiveLen, i;
	uint32_t speed, time;
	int16_t index = -1;
	uint8_t *receiveArray = UartGetRxBuffer(UART_COM1);
	
	if (param == NULL) {
		taskENTER_CRITICAL();
		showStatus();
		taskEXIT_CRITICAL();
		return;
	}
	
	if (strcmp(param->name, sParSpeed) == 0) {
		speed = StrGetDec((char *)&receiveArray[param->index], 0, 10);
		HardSPISetSpeed(HARD_SPI_CH0, speed);
		UartPrintf(UART_COM1, "spi0: speed ok, %d\r\n", speed);
	}
	else if (strcmp(param->name, sParSend) == 0) {
		receiveLen = UartGetReceiveLen(UART_COM1);
		xEventGroupSetBits(APPEventGroup, APP_EVENT_SPI_CMD_RX);
		if (rxEn && (rxSize != 0)) {
			HardSPISendReceiveByteArrayPDMA(HARD_SPI_CH0, (uint32_t)&(receiveArray[param->index+1]), receiveLen - param->index - 1, (uint32_t)sReceive, rxSize);
		}
		else {
			HardSPISendByteArrayPDMA(HARD_SPI_CH0, (uint32_t)&(receiveArray[param->index+1]), receiveLen - param->index - 1);
		}
	}
	else if (strcmp(param->name, sParChar) == 0) {
		if (clearArray) {
			for (i = 0; i < SPI_CMD_ARRAY_SIZE/4; i++) {
				sSPIArray->wordArray[i] = 0;
			}
		}
		receiveLen = StrChar2Hex((char *)receiveArray, param->index, SPI_CMD_ARRAY_SIZE/2, sSPIArray->halfWordArray, SPI_CMD_ARRAY_SIZE/2);
		xEventGroupSetBits(APPEventGroup, APP_EVENT_SPI_CMD_RX);
		if (rxEn && (rxSize != 0)) {
			HardSPISendReceiveHalfWordArrayPDMA(HARD_SPI_CH0, (uint32_t)sSPIArray->halfWordArray, receiveLen/4, (uint32_t)sReceive, rxSize/2);
		}
		else {
			HardSPISendHalfWordArrayPDMA(HARD_SPI_CH0, (uint32_t)sSPIArray->halfWordArray, receiveLen/4);
		}
		clearArray = 1;
	}
	else if (strcmp(param->name, sParBuf) == 0) {
		index = StrGetCHIndex((char *)&receiveArray[param->index], '-');
		/* No second level parameter, store all the data after the space into the sSPIArray */
		if (index == -1) {
			receiveLen = UartGetReceiveLen(UART_COM1);
			for (i = 0; i < (receiveLen - param->index - 1); i++) {
				sSPIArray->byteArray[i] = receiveArray[i+param->index+1];
			}
		}
		else {
			receiveLen = UartGetReceiveLen(UART_COM1);
			/* Second level parameter: spi0 -buf -char 
			 * This means that the string needs to be converted to a hex number */
			if (strncmp((char *)&receiveArray[param->index+1+index], sParChar, strlen(sParChar)) == 0) {
				index = StrGetCHIndex((char *)&receiveArray[param->index + 1], ' ');
				sSendLen = StrChar2Hex((char *)&receiveArray[param->index+1+index], 0, receiveLen - param->index - 1 - index, sSPIArray->halfWordArray, SPI_CMD_ARRAY_SIZE/2);
				sSendLen = sSendLen / 4;
			}
			/* Second level parameter: spi0 -buf -send
			 * This means that we need send buf's data */
			else if (strncmp((char *)&receiveArray[param->index+1+index], sParSend, strlen(sParSend)) == 0) {
                if (sDataWidth == 8) {
                    HardSPISendByteArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->byteArray, sSendLen);
                }
				else if (sDataWidth == 16) {
                    HardSPISendHalfWordArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->halfWordArray, sSendLen);
				}
                else {
                    HardSPISendWordArrayPDMA(HARD_SPI_CH0, (uint32_t)&sSPIArray->wordArray, sSendLen);
                }
            }
			/* Second level parameter: spi0 -buf -width
			 * This means setting the bit width of sSPIArray */
            else if (strncmp((char *)&receiveArray[param->index+1+index], sParWidth, strlen(sParWidth)) == 0) {
                i = StrGetDec((char *)&receiveArray[param->index+1+5], 0, 4);
				if ((i != 8) && (i != 16) && (i != 32)) {
					UartPuts(UART_COM1, "spi0 -buf -width: Not support width\r\n");
				}
				else {
					sDataWidth = i;
				}
            }
			/* Second level parameter: spi0 -buf -time 
			 * This means the data in buf needs to be sent periodically */
			else if (strncmp((char *)&receiveArray[param->index+1+index], sParTime, strlen(sParTime)) == 0) {
				time = StrGetDec((char *)&receiveArray[param->index+5], 0, 10);
				
				taskENTER_CRITICAL();
				
				if (i == 0) {
					if (parTimeTimerHandle != NULL) {
						xTimerDelete(parTimeTimerHandle, portMAX_DELAY);
						parTimeTimerHandle = NULL;
					}
				}
				else {
					if (parTimeTimerHandle == NULL) {
						parTimeTimerHandle = xTimerCreate(sParTime, time, pdTRUE, (void *)1, timerCallback);
						if (parTimeTimerHandle == NULL) {
							UartPuts(UART_COM1, "spi0 -buf -time:\r\n");
							UartPuts(UART_COM1, "\tError: can't create timer\r\n");
						}
						else {
							xTimerStart(parTimeTimerHandle, 0);
						}
					}
					else {
						xEventGroupSetBits(APPEventGroup, APP_EVENT_SPI_CMD_TIME);
						newTime = time;
						// xTimerChangePeriod(parTimeTimerHandle, i, 0);
					}
				}
				
				taskEXIT_CRITICAL();
			}
			else {
				UartPuts(UART_COM1, "spi0 -buf: Unknow param");
			}
		}
	}
	else if (strcmp(param->name, sParRx) == 0) {
		if (strncmp((char *)&receiveArray[param->index+1], strEn, 2) == 0) {
			HardSPIEnPDMAInterrupt(HARD_SPI_CH0);
			vTaskResume(listenTaskHandle);
			rxEn = 1;
		}
		else if (strncmp((char *)&receiveArray[param->index+1], strUnen, 4) == 0) {
			HardSPIDisPDMAInterrupt(HARD_SPI_CH0);
			vTaskSuspend(listenTaskHandle);
			rxEn = 0;
			if (sReceive != NULL) {
				vPortFree(sReceive);
				sReceive = NULL;
				rxSize = 0;
			}
		}
		else {
			UartPuts(UART_COM1, "rx: No match param\r\n");
		}
	}
	else if (strcmp(param->name, sParSize) == 0) {
		i = StrGetDec((char *)&receiveArray[param->index], 0, 8);
		if (i == 0) {
			if (sReceive != NULL) {
				vPortFree(sReceive);
				rxSize = 0;
			}
		}
		else {
			if (sReceive != NULL) {
				vPortFree(sReceive);
			}
			sReceive = (uint8_t *)pvPortMalloc(i);
			if (sReceive == NULL) {
				UartPuts(UART_COM1, "size: Can't malloc memory\r\n");
				rxEn = 0;
			}
			else {
				rxSize = i;
			}
		}
	}
	else {
		UartPuts(UART_COM1, "spi0: Unknow param\r\n");
	}
}

void SpiCmdInit(void)
{
	static _SPIArray array;
	
	uint16_t i;
	
	for (i = 0; i < SPI_CMD_ARRAY_SIZE / 4; i++) {
		array.wordArray[i] = 0;
	}

	sSPIArray = &array;
	
	CmdAppend(sCmdSpi0, cmdCallback);
	CmdAppendParam(sCmdSpi0, sParSpeed);
	CmdAppendParam(sCmdSpi0, sParSend);
	CmdAppendParam(sCmdSpi0, sParChar);
	CmdAppendParam(sCmdSpi0, sParBuf);
	CmdAppendParam(sCmdSpi0, sParRx);
	CmdAppendParam(sCmdSpi0, sParSize);
}

void SpiCmdCreateListenTask(uint8_t priority)
{
	BaseType_t result = pdPASS;
	
	result = xTaskCreate(listenTask, "listenTask", 128, NULL, priority, &listenTaskHandle);
	if (result != pdPASS) {
		UartPrintf(UART_COM1, "listenTask create fail\r\n");
	}
	else {
		vTaskSuspend(listenTaskHandle);
	}
	SpiCmdEventGroup = xEventGroupCreate();
}
